/* _____________________________________________________________________________
 *
 *                     C A R T E    U B W 3 2    M X 7 9 5
 * _____________________________________________________________________________
 *
 * Titre            : Démo du module RTCC
 * Version          : v01r00
 * Date             : 27/10/2013
 * Auteur           : Arnauld BIGANZOLI (AB)
 * Contact          : arnauld.biganzoli@gmail.com
 * Source du PGM    : l'auteur
 * Carte MCU        : chipKIT Max32™ Prototyping Platform + chipKIT Network Shield™
 * Outils utilisés  : MPLAB® X IDE v1.90 (Tools used)
 * Compilateur      : Microchip XC32 v1.21 Compiler
 * MCU utilise      : PIC32MX795F512L
 * Horloge          : POSC   = Quartz Q1= 8 MHz
 * SystemClock      : SYSCLK = POSC / FPLLIDIV * FPLLMUL / FPLLODIV, soit 8e+6/2*20/1= 80 MHz
 * PeripheralClock  : PBCLK  = SYSCLK / FPBDIV, soit 80e+6/8= 10 MHz (10e+6)
 * Clocking device  : TCY    = 1 / PBCLK, soit 1/80e+6= 12,5 ns (12,5e-9)
 * Programmateur    : PICkit 3
 * Alimentation     : alimentation par liaison USB
 * 
 *
 * Historique des modifications / Change history :
 * ----------------------------------------------
 *
 * Rev      Date        Auteur      Commentaire
 * --------------------------------------------------------------------------
 * 01.00    27/10/2013      AB      Première version
 * 
 * 
 * Software License Agreement :
 * ---------------------------
 * THIS SOFTWARE IS PROVIDED IN AN "AS IS" CONDITION. NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
 * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 * 
 * Description :
 * ------------
 * Démo RTCC "Real-Time Clock and Calendar"
 * Requirements: External 32.768 kHz Clock Crystal (IC6 on chipKIT Network Shield™)
 * --> Seconds Clock (or Alarm Pulse) Output on RTCC pin
 *     RTCCONbits.RTCOE = 1;    // RTCC clock output enabled – clock presented onto an I/O
 * --> Alarm Intervals is configured every 10 Seconds
 * _____________________________________________________________________________
 */

// TOTO: > Vérifier fonctionnement de la broche RTCC clock output --> RTCC/EMDIO/AEMDIO/IC1/RD8
// FIXME: > Lors de la configuration de l'alarm celle-ci déclenche l'interruption avant même quelle soit arrivé à terme !


/** I N C L U D E S ***********************************************************/
#include <plib.h>       // Master header file for all peripheral library includes

#ifndef _RTCC
    #error "This example needs a PIC32MX processor with RTCC peripheral present. Aborting build!"
#endif  // _RTCC


/** C O N F I G U R A T I O N   B I T S ***************************************/
// Configuration Bit settings:
// SYSCLK = 80 MHz (8MHz Crystal / FPLLIDIV * FPLLMUL / FPLLODIV) --> 8e+6/2*20/1= 80 000 000 Hz
// PBCLK  = 10 MHz (SYSCLK / FPBDIV)
// Primary Osc w/PLL (XT+,HS+,EC+PLL)
// WDT OFF
/// Other options are don't care

/* Configuration Bit settings: Clock Control Logic (for SYSCLK) */
#pragma config FNOSC    = PRIPLL        // Oscillator Selection
#pragma config FCKSM    = CSDCMD        /// Clock Switching & Fail Safe Clock Monitor --> Clock Switch Disable, FSCM Disabled
#pragma config OSCIOFNC = OFF           /// CLKO Enable
#pragma config IESO     = OFF           /// Internal/External Switch-over

/* Configuration Bit settings: Primary Oscillator (POSC) */
#pragma config POSCMOD  = HS            // Primary Oscillator
#pragma config FPLLIDIV = DIV_2         // PLL Input Divider
#pragma config FPLLMUL  = MUL_20        // PLL Multiplier
#pragma config FPLLODIV = DIV_1         // PLL Output Divider

/* Configuration Bit settings: Secondary Oscillator (SOSC) */
#pragma config FSOSCEN  = OFF           /// Secondary Oscillator Enable (KLO was off)

/* Configuration Bit settings: USB PLL */
#pragma config UPLLEN   = ON            /// USB PLL Enabled
#pragma config UPLLIDIV = DIV_2         /// USB PLL Input Divider

/* Configuration Bit settings: Peripheral Clock (PBCLK) */
#pragma config FPBDIV   = DIV_8         // Peripheral Clock divisor

/* Configuration Bit settings: Watchdog Timer */
#pragma config FWDTEN   = OFF           // Watchdog Timer
//#pragma config WDTPS    = PS1           /// Watchdog Timer Postscale

/* Configuration Bit settings: Write Protect */
#pragma config CP       = OFF           /// Code Protect
#pragma config BWP      = OFF           /// Boot Flash Write Protect
#pragma config PWP      = OFF           /// Program Flash Write Protect

/* Configuration Bit settings: ICE/ICD */
#pragma config ICESEL   = ICS_PGx2      /// ICE/ICD Comm Channel Select


/** D E F I N I T I O N S *****************************************************/
// Clock frequency values
// These directly influence timed events using the Tick module.  They also are used for UART and SPI baud rate generation.
#define SYS_FREQ                (80000000ul)            // Q1: 8 MHz
#define GetSystemClock()        (SYS_FREQ)              // Hz, POSC(8 MHz) / FPLLIDIV * FPLLMUL / FPLLODIV, soit 8e+6/2*15/1= 60 MHz
#define GetInstructionClock()   (GetSystemClock()/1)    // Normally GetSystemClock()/4 for PIC18, GetSystemClock()/2 for PIC24/dsPIC, and GetSystemClock()/1 for PIC32.  Might need changing if using Doze modes.
#define GetPeripheralClock()    (GetSystemClock()/(1 << OSCCONbits.PBDIV)) // Normally GetSystemClock()/4 for PIC18, GetSystemClock()/2 for PIC24/dsPIC, and GetSystemClock()/1 for PIC32.  Divisor may be different if using a PIC32 since it's configurable.


#define UxBRG_9600_BAUD (259)               // configure la vitesse (BAUD) 9600 N 8 1 at PeripheralClock = 10 MHz
// UxBRG = (PeripheralClock / (4 x 9600)) - 1
//       = (10e6/(4/*9600))-1
//       = 259.4

#define UxBRG_19200_BAUD (1041)             // configure la vitesse (BAUD) 19200 N 8 1 at PeripheralClock = 80 MHz
// UxBRG = (PeripheralClock / (4 x 19200)) - 1
//       = (80e6/(4/*19200))-1
//       = 1040.7


/** Hardware I/O pin mappings *************************************************/
// Hardware specific definitions for chipKIT Max32™ Prototyping Platform
// http://www.digilentinc.com/Products/Detail.cfm?NavPath=2,892,894&Prod=CHIPKIT-MAX32
// http://www.digilentinc.com/Data/Products/CHIPKIT-NETWORK-SHIELD/chipKIT%20Network%20Shield_rm.pdf
// A 32.768 Khz oscillator is provided to use as a clock source for the Real Time Clock/Calendar (RTCC) peripheral
// on the Max32 board, this signal connects to SOSCI/CN1/RC13 (chipKIT Pin 75)
// Real-Time Clock alarm output --> RTCC/EMDIO/AEMDIO/IC1/RD8

/** LED ***********************************************************************/
#define LD4_TRIS            (TRISAbits.TRISA3)  // LED LD4 GREEN
#define LD4_IO              (LATAbits.LATA3)    // SDA2/RA3
#define LD5_TRIS            (TRISCbits.TRISC1)  // LED LD5 YELLOW
#define LD5_IO              (LATCbits.LATC1)    // T2CK/RC1

#define LED1_TRIS           (LD4_TRIS)
#define LED1                (LD4_IO)
#define LED2_TRIS           (LD5_TRIS)  // Ref: CON3-2 --> alternative a RD1
#define LED2                (LD5_IO)

#define mInitAllLEDs()      {LED1 = 0; LED1_TRIS = 0; LED2 = 0; LED2_TRIS = 0;}
#define mLED_1_On()         (LED1 = 1)
#define mLED_2_On()         (LED2 = 1)
#define mLED_1_Off()        (LED1 = 0)
#define mLED_2_Off()        (LED2 = 0)
#define mLED_1_Toggle()     (LED1 = !LED1)
#define mLED_2_Toggle()     (LED2 = !LED2)

// for RTCC fonction: BCD "Binary Coded Decimal"
#define mRTCCDec2Bin(Dec)   (10 * (Dec >> 4) + (Dec & 0x0F))    // permet de convertir un nombre au format BCD en un nombre binaire
#define mRTCCBin2Dec(Bin)   (((Bin / 10) << 4) | (Bin % 10))    // permet de convertir un nombre au format binaire en un nombre BCD


/** V A R I A B L E S *********************************************************/
// for RTCC fonction:
char Day[7][4]		= {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
char Month[13][4]	= {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

rtccTime CurrentTime, AlarmTime;    // time structure
rtccDate CurrentDate, AlarmDate;    // date structure

// for serial communication:
#define LENGHT_STR          (64)        // longueur de la chaine de caracteres, ajouter 1 pour le caractere NULL de fin de chaine
CHAR8 str_Message[LENGHT_STR];          // variable globale, permettant l'affichage d'une chaine de caractere


/** P R I V A T E   P R O T O T Y P E S ***************************************/
// Local Function Prototypes:
void Init_UART1(void);
void Putc_UART1(unsigned char c);
unsigned char Getc_UART1(void);
void Puts_UART1(char *s);


/** D E C L A R A T I O N S ***************************************************/
// "REGISTER 19-2: UxSTA: UARTx STATUS AND CONTROL REGISTER" DS60001156H-page 22
void Init_UART1(void)
{
    U1BRG    = UxBRG_9600_BAUD;
    U1STA    = 0;
    U1MODE   = 0x8008;          // init UART
    U1STASET = 0x1400;          // enable transmitter
}


void Putc_UART1(unsigned char c)
{
    while(U1STAbits.UTXBF);     // wait until busy flag of transmitter is cleared
    U1TXREG = c;
}


unsigned char Getc_UART1(void)
{
    while(!U1STAbits.URXDA);    // wait for receive buffer has data
    return U1RXREG;
}


void Puts_UART1(char *s)
{
    /* [EN] Send a data as the value pointed to by the pointer is not null */
    /* [FR] Envoyer une donnée tant que la valeur pointée par le pointer n'est pas null */
    while(*s)
    {
        Putc_UART1(*s++);
    }
//    Putc_UART1('\r');  // 0x0D (CR) --> 13
//    Putc_UART1('\n');  // 0x0A (LF) --> 10
}


/* RTCC Interrupt Service Routine: Alarm interrupt management */
//void __ISR_SINGLE__ RTCCInterrupt( void)
//void __ISR(_RTCC_VECTOR, ipl4) __RTCCInterrupt(void)
void __ISR(_RTCC_VECTOR, ipl4) RTCCInterrupt(void)
{	
    RtccGetTimeDate(&CurrentTime, &CurrentDate); // get current time and date
//    sprintf(str_Message, "\nAlarm on %02d:%02d:%02d - ", CurrentTime.hour, CurrentTime.min, CurrentTime.sec);
    sprintf(str_Message, ">> Alarm at %02d:%02d:%02d - ", mRTCCDec2Bin(CurrentTime.hour), mRTCCDec2Bin(CurrentTime.min), mRTCCDec2Bin(CurrentTime.sec) );
    Puts_UART1(str_Message); // send string
    sprintf(str_Message, "%s, %02d %s %04d << <\r\n\n> ", Day[CurrentDate.wday], mRTCCDec2Bin(CurrentDate.mday), Month[mRTCCDec2Bin(CurrentDate.mon)], mRTCCDec2Bin(CurrentDate.year)+2000 );
    Puts_UART1(str_Message); // send string

    mRTCCClearIntFlag();    // clear the interrupt flag before exiting the service routine

} //RTCCInterrupt


/** M A I N   F U N C T I O N *************************************************/
void main(void)
{
    unsigned char receive_byte;             // local variable for received character
    unsigned int i, j;                      // [EN] temporary variables for the routine timing; [FR] variables temporaires pour la routine de temporisation

/** Configure SYSTEM */
    // Configure the device for maximum performance but do not change the PBDIV
	// Given the options, this function will change the flash wait states, RAM
	// wait state and enable prefetch cache but will not change the PBDIV.
	// The PBDIV value is already set via the pragma FPBDIV option above..
	SYSTEMConfig(SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);

    /**
     * The JTAG is on by default on POR.  A PIC32 Starter Kit uses the JTAG, but
     * for other debug tool use, like ICD 3 and Real ICE, the JTAG should be off
     * to free up the JTAG I/O
     */
    #if !defined(PIC32_STARTER_KIT)             // Not for SKDE PIC32
        mJTAGPortEnable(DEBUG_JTAGPORT_OFF);    // Disable JTAG port, free up PORTA
    #endif

/** Initialisation des LED de visualisation du bon fonctionnement de l'alarme */
    mInitAllLEDs();

/** Initialisation du module UART */
    Init_UART1();

/** Test de l'UART */
    // [EN] Delays before transmit the string character to the PC
    // [FR] Temporisation avant de transmettre la chaîne de caractères vers le PC
    for (i = 0; i < 50; i++)
    {
        for (j = 0; j < 10000; j++);
    }
//    for(i=0; i<0xFFFF; i++);     // [EN] wait a few moments before the message / [FR] attendre quelques instants avant de transmettre le message
    Puts_UART1("=== Firmware compiled at " __TIME__ ", " __DATE__" ===\r\n\n");
    Puts_UART1("UART1 Serial Port configured at 9600 baud\r\n"); // send string

/* Initialisation du module RTCC */
    RtccInit();                 // init the RTCC
    
    // waiting for RTCC clock source ready (optional):
    while(RtccGetClkStat() != RTCC_CLK_ON); // wait for the SOSC to be actually running and RTCC to have its clock source
                                            // could wait here at most 32ms

	// set time and date to 16:44:59, lundi 28 octobre 2013:
//    CurrentTime.l       = 0;        // clear CurrentTime structure
//    CurrentTime.hour    = 0x16;     // 16 h
//    CurrentTime.min     = 0x44;     // 44 min
//    CurrentTime.sec     = 0x59;     // 59 sec
//
//    CurrentDate.year    = 0x13;     // 2013
//    CurrentDate.mon     = 0x10;     // october
//    CurrentDate.mday    = 0x28;     // 28
//    CurrentDate.wday    = 1;        // monday
// or
//    CurrentDate.wday    = RtccWeekDay(2013, 10, 28); // calculates the week of the day
//    RtccSetTimeDate(CurrentTime.l, CurrentDate.l);
//    RtccSetCalibration(200);	// adjust the RTCC timing: value to calibrate with at each minute
	// or setting directly the current date:
	RtccOpen(0x16445900, 0x13102801, 0);	// time is MSb: hour, min, sec, rsvd (please note that the rsvd field has to be 0 in the time field!)
                                            // date is MSb: year, mon, mday, wday
                                            // drift = 0 --> add n pulse every minute to adjust time

    // enabling the RTCC output pin:
	RtccSelectPulseOutput(1);               // select the seconds clock pulse as the function of the RTCC output pin
//	RtccSelectPulseOutput(0);               // select the alarm pulse as the function of the RTCC output pin
	RtccOutputEnable(1);                    // enable the Output pin of the RTCC

    // configure the RTCC alarm:
    RtccGetTimeDate(&CurrentTime, &CurrentDate); // get current time and date
    AlarmTime.l = CurrentTime.l;            // synchronize alarm time with current time
    AlarmDate.l = CurrentDate.l;            // synchronize alarm date with current date
    AlarmTime.min = mRTCCBin2Dec(46);       // set alarm at 16 h 46 min 00 sec :
    AlarmTime.sec = 0;                      // 0x164600
    sprintf(str_Message, "RTCC Alarm set to %02d:%02d:%02d - ", mRTCCDec2Bin(AlarmTime.hour), mRTCCDec2Bin(AlarmTime.min), mRTCCDec2Bin(AlarmTime.sec) );
    Puts_UART1(str_Message); // send string
    sprintf(str_Message, "%s, %02d %s\r\n\n> ", Day[AlarmDate.wday], mRTCCDec2Bin(AlarmDate.mday), Month[mRTCCDec2Bin(AlarmDate.mon)] );
    Puts_UART1(str_Message); // send string

	// other things we may do with the alarm...
//	RtccChimeEnable();                      // enable indefinite repeats
//	RtccSetAlarmRptCount(1);                // set the initial repeat count
//  RtccSetAlarmRpt(RTCC_RPT_TEN_SEC);      // enable repeat rate, check the second field
	RtccSetAlarmTimeDate(AlarmTime.l, AlarmDate.l); // set the alarm time
//    RtccSetAlarmTimeDate(0x164600, 0x131001);
    mRTCCClearIntFlag();                    // clear the interrupt flag
	RtccAlarmEnable();                      // enable the alarm
    
	// enabling the RTCC alarm interrupts:
	INTSetVectorPriority(INT_RTCC_VECTOR, INT_PRIORITY_LEVEL_4);        // set the RTCC priority in the INT controller
	INTSetVectorSubPriority(INT_RTCC_VECTOR, INT_SUB_PRIORITY_LEVEL_1); // set the RTCC sub-priority in the INT controller
	INTEnable(INT_RTCC, INT_ENABLED);                                   // enable the RTCC event interrupts in the INT controller.

/** Configure Interrupt: Enabling Multi-Vectored Mode */
    INTEnableSystemMultiVectoredInt();      // Enable multi-vector interrupts
    // or
//    INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR); // configure for multi-vectored mode
//    INTEnableInterrupts();                  // enable interrupts

// ...
    
/** Boucle principale */
    while(1)
    {
        receive_byte = Getc_UART1();       // waiting for receive byte from PC
        
        switch (receive_byte)
        {
            case '0':
                RtccChimeDisable();         // disables the chime alarm of the RTCC device
                Puts_UART1("\nCommand Alarm Chime OFF, done.\r\n");
                RtccAlarmDisable();
                Puts_UART1("Command Alarm OFF, done.\r\n\n> ");
                mLED_2_Off();
                break;

            case '1':
//                RtccSetAlarmRpt(RTCC_RPT_TEN_SEC);      // enable repeat rate, check the second field
                RtccChimeEnable();          // enable indefinite repeats
                Puts_UART1("\nCommand Alarm Chime ON, done.\r\n");
                RtccAlarmEnable();
                Puts_UART1("Command Alarm ON, done.\r\n\n> ");
                mLED_2_On();
                break;

            case 't':
            case 'T':
                mLED_1_Toggle();
                RtccGetTimeDate(&CurrentTime, &CurrentDate); // get current time and date
                sprintf(str_Message, "\nRead RTCC: %02d:%02d:%02d - ", mRTCCDec2Bin(CurrentTime.hour), mRTCCDec2Bin(CurrentTime.min), mRTCCDec2Bin(CurrentTime.sec) );
                Puts_UART1(str_Message);                    // send string
                sprintf(str_Message, "%s, %02d %s %04d\r\n\n> ", Day[CurrentDate.wday], mRTCCDec2Bin(CurrentDate.mday), Month[mRTCCDec2Bin(CurrentDate.mon)], mRTCCDec2Bin(CurrentDate.year)+2000 );
                Puts_UART1(str_Message);                    // send string
                break;

            case 'a':
            case 'A':
                // Set Alarm to the next min:
                RtccGetTimeDate(&CurrentTime, &CurrentDate); // get current time and date
                AlarmTime.l = CurrentTime.l;            // synchronize alarm time with current time
                AlarmDate.l = CurrentDate.l;            // synchronize alarm date with current date
                if (AlarmTime.min == 59)
                {
                    AlarmTime.min = 0;
                    if (AlarmTime.hour == 0x23)
                    {
                        AlarmTime.hour = 0;
                    }
                    else
                    {
                        AlarmTime.hour++;   // FIXME: il faut certeinnement passer du BCD au binaire, incrémenter, puis repasser au BCD
                    }
                }
                else
                {
                    AlarmTime.min++;        // FIXME: il faut certeinnement passer du BCD au binaire, incrémenter, puis repasser au BCD
                }
                sprintf(str_Message, "\nRTCC Alarm set to %02d:%02d:%02d - ", mRTCCDec2Bin(AlarmTime.hour), mRTCCDec2Bin(AlarmTime.min), mRTCCDec2Bin(AlarmTime.sec) );
                Puts_UART1(str_Message); // send string
                sprintf(str_Message, "%s, %02d %s\r\n\n> ", Day[AlarmDate.wday], mRTCCDec2Bin(AlarmDate.mday), Month[mRTCCDec2Bin(AlarmDate.mon)] );
                Puts_UART1(str_Message); // send string
//                RtccChimeEnable();                      // enable indefinite repeats
//                RtccSetAlarmRptCount(1);                // set the initial repeat count
//                RtccSetAlarmRpt(RTCC_RPT_TEN_SEC);      // enable repeat rate, check the second field
                RtccSetAlarmTimeDate(AlarmTime.l, AlarmDate.l); // set the alarm time
                RtccAlarmEnable();                      // enable the alarm
                break;

            case 'c':
            case 'C':
                // check if alarm is enabled and current status of the alarm chime:
                sprintf(str_Message, "\nAlarm Enable = %d - Chime Enable = %d\r\n\n> ", RtccGetAlarmEnable(), RtccGetChimeEnable() );
                Puts_UART1(str_Message);                    // send string
                break;

            default:
                Puts_UART1("\nCommand invalid!\r\n\n> ");   // message "commande invalide"
//                Nop();
        }
    } // end of while(1)
} // end of main()


/** EOF _main_RTCC_demo_v01r00.c **********************************************/

/*
> T<LF>
Read RTCC: 16:45:59 - Sun, 28     2013<CR><LF>
> T<LF>
Read RTCC: 16:46:00 - Sun, 28     2013<CR><LF>

 */
